package com.wang.socket;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import com.wang.packpojo.ConMsg;
import com.wang.util.EmptyUtil;
import com.wang.util.GetByteEncodeUtil;
import com.wang.util.ModBusUtil;
import com.wang.util.PrintUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;

import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.List;

/**
 * 自定义解码器
 *
 * @author wcy   添加功能解析功能
 */
public class CustomDecoder5 extends ByteToMessageDecoder {

    private final int BASE_LENGTH = 1;

    private String heart = "METER";
    private String reg = "DSDEVREG";
    int num = 0;

    /**
     * 十六进制字符串数组解析
     *
     * @param ctx
     * @param in   传入数据
     * @param list 添加解码消息
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> list) throws Exception {


        int beginIndex;//记录包开始位置


        while (true) {

            //获取包头开始的index
            beginIndex = in.readerIndex();
            // 将当前的readerIndex备份到markedReaderIndex中
            in.markReaderIndex();


            //获取字节可读取长度
            int reablesLength = in.readableBytes();

            //1、判断是不是注册包
            //--------------------------------------------

            //注册包处理
            if(reablesLength >= reg.getBytes().length){
                //读取前8位字节 判断是不是注册包数据
                byte[] bytes2 = new byte[reg.getBytes().length];
                in.readBytes(bytes2);

                String encoding1 = GetByteEncodeUtil.getEncoding(bytes2);
                String data2 = new String(bytes2, encoding1);
                if (data2.equals(reg)) {
                    //继续往下读  读到；结束
                    //定义下标
                    int index = 0;
                    //定义包含;的标志
                    boolean containFlag = false;

                    for (int i = 0; i < reablesLength - reg.getBytes().length; i++) {

                        //读取一个字节
                        byte[] bytesFlag = new byte[1];
                        in.readBytes(bytesFlag);

                        String encoding2 = GetByteEncodeUtil.getEncoding(bytesFlag);
                        String flag = new String(bytesFlag, encoding2);

                        if (flag.equals(";")) {
                            index = i;
                            containFlag = true;
                            break;
                        }
                    }
                    if (containFlag) {
                        //重置读取字节位置
                        in.resetReaderIndex();
                        //读取完整的注册包数据
                        byte[] regDatas = new byte[reg.getBytes().length + index + 1];
                        in.readBytes(regDatas);

                        String encoding3 = GetByteEncodeUtil.getEncoding(regDatas);
                        String ctxData = new String(regDatas, encoding3);


                        //添加数据
                        list.add(ctxData.replace(";", ""));
                        //回收已读字节
                        in.discardReadBytes();
                        return;
                    }else {
                        //重置读取点
                        in.readerIndex(beginIndex);
                    }
                }else {
                    //重置读取点
                    in.readerIndex(beginIndex);
                }

            }

            //2、 判断心跳包 先判断全局变量已注册的是否包含此设备
            String address = getRemoteAddress(ctx);



            String isContain = "false";
            ConMsg conMsg = null;
            if (SocketMap.mRegPassDeviceMap.containsKey(address)) {
                isContain = "mContain";
                conMsg = SocketMap.mRegPassDeviceMap.get(address);

            }
            if (SocketMap.jRegPassDeviceMap.containsKey(address)) {
                isContain = "jContain";
                conMsg = SocketMap.jRegPassDeviceMap.get(address);
            }
            if (isContain.equals("false")) {
                PrintUtil.print("设备未注册，收到了数据");
                return;
            }
            //---------------------------------
            //2、判断是不是心跳包
            if (!isContain.equals("false")) {


                String heartBeat = conMsg.getConHeart().getHeartBeat();
                if (reablesLength >= heartBeat.getBytes().length) {
                    //先读前心跳包长度 判断是是不是心跳包数据
                    byte[] bytes = new byte[heartBeat.getBytes().length];
                    in.readBytes(bytes);

                    String encoding4 = GetByteEncodeUtil.getEncoding(bytes);
                    String data = new String(bytes, encoding4);
                    if (data.equals(heartBeat)) {

                        /*String encoding5 = GetByteEncodeUtil.getEncoding(bytes);
                        String ctxData = new String(bytes, encoding5);*/

                        //添加数据

                        list.add(data);
                        //回收已读字节
                        in.discardReadBytes();
                        return;
                    }else {
                        //重置读取点
                        in.readerIndex(beginIndex);
                    }

                }

            }

            //3、判断是不是json数据
            //--------------------------------
            if (isContain.equals("jContain")) {


                PrintUtil.print("json设备");

                //方式  读取左大括号 +1   读取到右括号-1
                //先读取第一个字节 是否是 { 如果是依次读字节
                byte[] byteleft = new byte[1];
                in.readBytes(byteleft);

                String encoding6 = GetByteEncodeUtil.getEncoding(byteleft);
                String firstData = new String(byteleft, encoding6);
                if (firstData.equals("{")) {
                    //下标
                    int indexFlag = 0;
                    int intFlag = 0;


                    indexFlag++;
                    intFlag++;
                    //依次往下读
                    //读取一个字节 -1 代表已经读过一个字节
                    for (int i = 0; i < reablesLength - 1; i++) {

                        byte[] bytesFlag = new byte[1];
                        in.readBytes(bytesFlag);
                        indexFlag++;
                        String encoding7 = GetByteEncodeUtil.getEncoding(bytesFlag);
                        String flag = new String(bytesFlag, encoding7);
                        if (flag.equals("{")) {
                            //标识+1
                            intFlag++;
                        }
                        if (flag.equals("}")) {
                            //标识-1
                            intFlag--;
                        }
                        if (intFlag == 0) {
                            //如果标识为0 查询到所有json数据
                            break;
                        }
                    }

                    if (intFlag == 0) {


                        //截取数据
                        //重置字节读取下标
                        in.resetReaderIndex();
                        byte[] jsonBytes = new byte[indexFlag];
                        in.readBytes(jsonBytes);
                        String encoding8 = GetByteEncodeUtil.getEncoding(jsonBytes);
                        System.out.println("编码格式:" + encoding8);
                        String jsonData = new String(jsonBytes, encoding8);

                        //添加数据
                        list.add(jsonData);
                        //回收已读字节
                        in.discardReadBytes();

                        return;

                    } else {

                        //如果不包含 代表数据不全，返回读的指针 等待下一次的数据到期在读
                        in.readerIndex(beginIndex);
                        return;
                    }


                }else {
                    //回收已读字节
                    in.discardReadBytes();
                    return;
                    //如果不包含 代表数据不全，返回读的指针 等待下一次的数据到期在读
                   /* in.readerIndex(beginIndex);
                    return;*/
                }
            }
            //4、判断是不是modbus数据
            if (isContain.equals("mContain")) {
                //最后一步 modbus数据读取

                if (reablesLength >= 4){
                    //截取功能码 判断是不是03 读取数据
                    byte[] funMdatas = new byte[2];
                    in.readBytes(funMdatas);
                    String addressCode = bytes2HexString(funMdatas).split(" ")[0];
                    String funCode = bytes2HexString(funMdatas).split(" ")[1];

                    System.out.println("地址位"+addressCode+"功能码"+funCode);

                    if (funCode.equals("03")) {  //如果是读取数据

                        //重置读取位
                        in.resetReaderIndex();

                        //modbus数据读取前3个字节 获取数据长度
                        byte[] mdatas = new byte[3];
                        in.readBytes(mdatas);
                        //截取数据位长度
                        String mlength = bytes2HexString(mdatas).split(" ")[2];


                        //重置读取位
                        in.resetReaderIndex();

                        Integer length = hex2decimal(mlength);

                        int totalDataLength = 3 + length + 2;  //地址 +功能 +数据长度  3   加上数据长度 +  校验位长度2
                        //如果可读数据位长度小于  数据位长度  返回 等待下一次数据到期在读
                        if (reablesLength < totalDataLength) {
                            in.readerIndex(beginIndex);
                            return;
                        }
                        byte[] mdatas2 = new byte[totalDataLength];

                        in.readBytes(mdatas2);


                        //计算modbus数据合法性
                        //先将bytes 转换成16进制字符串
                        String baseData = ModBusUtil.bytes2HexString(mdatas2);


                        String baseDataStr = baseData.replaceAll(" ", "");
                        String[] baseDatas = baseData.split(" ");

                        //要校验的数据
                        String[] checkDatas = Arrays.copyOfRange(baseDatas, 0, baseDatas.length - 2);
                        StringBuffer sb1 = new StringBuffer();
                        for (String s : checkDatas) {
                            sb1.append(s);
                        }

                        byte[] checkBytes = ModBusUtil.HexString2Bytes(sb1.toString());


                        //计算crc值
                        String checkCrc2 = ModBusUtil.getCRC3(checkBytes);
                        //截取原有crc值
                        String checkCrc1 = baseDataStr.substring(baseDataStr.length() - 4);

                        if (checkCrc1.equals(checkCrc2)) {
                            //添加数据
                            list.add(baseData);

                            //回收已读字节
                            in.discardReadBytes();
                            break;

                        } else {
                            break;

                        }
                    } else {
                        //重置读取位
                        in.resetReaderIndex();

                        Integer readLength = 5; //定义modbus最小长度为5

                        String data = null;
                        for (int i = readLength; i <= reablesLength; i++) {


                            byte[] mdatas1 = new byte[i];  //读取i+2位  i为数据位  2为crc校验数据
                            in.readBytes(mdatas1);
                            String baseData = ModBusUtil.bytes2HexString(mdatas1);
                            //判读crc
                            String[] datas = baseData.split(" ");
                            StringBuilder sbData = new StringBuilder();
                            StringBuilder sbCrc = new StringBuilder();

                            for (int j = 0; j < datas.length; j++) {
                                if (j < datas.length - 2) {
                                    sbData.append(datas[j]);
                                } else {
                                    sbCrc.append(datas[j]);
                                }
                            }

                            //计算crc
                            byte[] bytes1 = ModBusUtil.HexString2Bytes(sbData.toString());
                            String crc = ModBusUtil.getCRC3(bytes1);

                            if (crc.equals(sbCrc.toString())) {
                                data = baseData;
                                in.discardReadBytes(); //回收已读字节
                                break;
                            } else {
                                //重置读取位
                                in.resetReaderIndex();
                            }
                        }

                        if (EmptyUtil.isNotNull(data)) {
                            //读取一个完整的数据块
                            list.add(data);
                            return;
                        } else {
                            //重置读取位
                            //in.resetReaderIndex();
                            //如果不包含 代表数据不全，返回读的指针 等待下一次的数据到期在读
                            in.readerIndex(beginIndex);
                            return;
                        }

                    }

                }

            }
        }


    }


    /*
     * 字节数组转16进制字符串
     */
    public static String bytes2HexString(byte[] b) {
        String r = "";
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            r += hex.toUpperCase() + " ";
        }
        return r;
    }

    // 16进制字符串转换10进制
    public static int hex2decimal(String hex) {
        return Integer.parseInt(hex, 16);
    }

    //获取连接ctx的地址
    public String getRemoteAddress(ChannelHandlerContext ctx) {
        InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = ipSocket.getAddress().getHostAddress();
        Integer port = ipSocket.getPort();
        String ip = clientIp + ":" + port;
        return ip;
    }

}


